#include "read_data.hh"
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <boost/lexical_cast.hpp>

using namespace std;
using boost::lexical_cast;

void read_locations(const std::vector<std::string>& locs, Adventure &adv) {
  int room;
  string tmp;

  for(register unsigned int i = 0; i < locs.size(); ++i) {
    istringstream in(locs[i]);
    
    in >> room;
    if(static_cast<unsigned int>(room) >= adv.locations.size()) adv.locations.resize(room + 1);
    Location &myloc = adv.locations[room];
    getline(in, tmp, '\x1e'); //Read until next rs
    getline(in, myloc.desc_short, '\x1e');
    getline(in, myloc.desc_long, '\x1e');
    getline(in, tmp, '\x1e'); //Read until next rs
    //We may reach EOF above, so clear stream flags!
    in.clear();
    in.str(tmp);
    while(getline(in, tmp, '\x1f')) {
      cout << tmp << endl;
    }
    myloc.visited = false;
  }
}

std::istream& skip_to_next(std::istream& in) {
  char ch;

  while(in >> ch) {
    if(ch == '\x01') return in; //SOH
  }
  return in;
  //throw out_of_range("skip_to_next: EOF");
}


std::string read_header(std::istream& in) {
  string hdrname;

  skip_to_next(in);
  getline(in, hdrname, '\x02'); //STX
  cerr << "read_header: '" << hdrname << "'\n";
  return hdrname;
}


template<class T>
std::istream& read(std::istream& in, std::vector<T>& vec) {
  std::vector<T> data;
  T obj;
  string tmp;
  int i = 0;

  while(!in.eof() && in.peek() != '\x03') {
    tmp = read_header(in);
    if(tmp != "\n") throw runtime_error("object header != \\n");
    read(in, obj);
    data.push_back(obj);
    cerr << i++ << endl;
  }
  vec = data;
  //Skip to next ETX
  return getline(in, tmp, '\x03');
}



std::istream& read(std::istream& in, std::map<std::string,int>& vec) {
  std::map<std::string,int> data;
  string tmp;
  string t1, t2;
  int i = 0;

  while(!in.eof() && in.peek() != '\x03') {
    getline(in, t1, '\x1f');
    getline(in, t2, '\x1e');
    data[t1] = lexical_cast<int>(t2);
    cerr << i++ << ": " << t1 << '=' << t2 << endl;
    if(in.get() != '\n') throw logic_error("no \\n after RS in string int map read");
  }
  vec = data;
  //Skip to next ETX
  return getline(in, tmp, '\x03');
}


std::istream& read(std::istream& in, std::map<int,int>& vec) {
  std::map<int,int> data;
  string tmp;
  int t1, t2;
  int i = 0;

  while(!in.eof() && in.peek() != '\x03') {
    getline(in, tmp, '\x1e');
    istringstream mapline(tmp);
    mapline >> t1 >> t2;
    data[t1] = t2;
    cerr << i++ << ": " << t1 << '=' << t2 << endl;
  }
  vec = data;
  //Skip to next ETX
  return getline(in, tmp, '\x03');
}


/*! \brief read a vector of strings
 *
 * Strings have a slightly different format than the default vector of
 * objects.
 *
 * \param in input file stream
 * \param vec vector of strings to save data in
 * \return input file stream
 */
std::istream& read(std::istream& in, std::vector<std::string>& vec) {
  std::vector<std::string> data;
  string tmp;
  int i = 0;

  while(!in.eof() && in.peek() != '\x03') {
    getline(in, tmp, '\x1e');
    data.push_back(tmp);
    cerr << i++ << endl;
    if(in.get() != '\n') throw logic_error("no \\n after RS in string vector read");
  }
  vec = data;
  //Skip to next ETX
  return getline(in, tmp, '\x03');
}


std::istream& read(std::istream& in, Object& vec) {
  string tmp;

  getline(in, tmp, '\x1e');
  vec.pos = lexical_cast<int>(tmp);
  getline(in, tmp, '\x1e');
  vec.weight = lexical_cast<int>(tmp);
  getline(in, vec.name, '\x1e');
  getline(in, vec.desc, '\x1e');
  //Ignore anything thereafter.
  return getline(in, tmp, '\x03');
}


std::istream& read(std::istream& in, Event& vec) {
  string tmp;

  getline(in, tmp, '\x1e');
  istringstream entries(tmp);
  entries >> vec.verb >> vec.adverb
	  >> vec.obj1 >> vec.adj1
	  >> vec.obj2 >> vec.adj2;
  while(in.peek() != '\x03') {
    tmp = read_header(in);
    ostringstream out;
    out << "unkown header '" << tmp << "' in read Event";
    throw invalid_argument(out.str());
  }
  //Ignore anything thereafter.
  return getline(in, tmp, '\x03');
}


std::istream& read(std::istream& in, Location& vec) {
  string tmp;

  getline(in, vec.desc_short, '\x1e');
  getline(in, vec.desc_long, '\x1e');
  getline(in, tmp, '\x1e');
  istringstream boolrd(tmp);
  boolrd >> vec.visited;
  while(in.peek() != '\x03') {
    tmp = read_header(in);
    if(tmp == "exits") {
      read(in, vec.exits);
    } else {
      ostringstream out;
      out << "unkown header '" << tmp << "' in read Location";
      throw invalid_argument(out.str());
    }
  }
  //Ignore anything thereafter.
  return getline(in, tmp, '\x03');
}


std::istream& read(std::istream& in, std::vector<Object>& vec) {
  std::vector<Object> data;
  Object obj;
  string tmp;
  int i = 0;

  while(!in.eof() && in.peek() != '\x03') {
    tmp = read_header(in);
    if(tmp != "\n") throw runtime_error("object header != \\n");
    read(in, obj);
    data.push_back(obj);
    cerr << i++ << endl;
  }
  vec = data;
  //Skip to next ETX
  return getline(in, tmp, '\x03');
}


std::istream& read(std::istream& in, Adventure& adv) {
  string hdr;

  while(!in.eof() && in.peek() != '\x04') {
    try {
      hdr = read_header(in);
    }
    catch(const out_of_range& excp) {
      throw;
      return in;
    }
    cerr << "hdr = '" << hdr << "'.\n";
    if(hdr == "Adventure") {
    } else if(hdr == "objects") {
      read(in, adv.objects);
    } else if(hdr == "verbs") {
      read(in, adv.verbs);
    } else if(hdr == "events") {
      read(in, adv.events);
    } else if(hdr == "msgs") {
      read(in, adv.msgs);
    } else if(hdr == "locations") {
      read(in, adv.locations);
    } else {
      cerr << "Warning! Ignoring unknown header '" << hdr << "'.\n";
    }
  }
  return in;
}

